//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_issue_api_serv

import (
	"context"

	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/issue_dao"
	"atomgit.com/openlinksaas/api-server/dao/project_dao"
	"atomgit.com/openlinksaas/api-server/utils"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_issue_api"
	"go.mongodb.org/mongo-driver/mongo"
)

func (apiImpl *ProjectIssueApiImpl) calcUserPerm(sessionItem *dao.SessionDbItem,
	projectItem *project_dao.ProjectInfoDbItem,
	roleItem *project_dao.RoleInfoDbItem,
	issueItem *issue_dao.IssueInfoDbItem,
	subIssueStatus *project_issue_api.SubIssueStatus) *project_issue_api.UserIssuePerm {
	if projectItem == nil || roleItem == nil {
		return &project_issue_api.UserIssuePerm{
			NextStateList: []project_issue_api.ISSUE_STATE{},
		}
	}
	if projectItem.Closed {
		return &project_issue_api.UserIssuePerm{
			NextStateList: []project_issue_api.ISSUE_STATE{},
		}
	}
	unDone := 0
	if subIssueStatus != nil {
		unDone = int(subIssueStatus.TotalCount) - int(subIssueStatus.DoneCount)
	}
	canAssignExecUser := false
	canAssignCheckUser := false
	canRemove := false
	canUpdate := false
	nextStateList := []project_issue_api.ISSUE_STATE{}
	if issueItem.State == int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_PLAN) {
		canAssignExecUser = true
		canAssignCheckUser = true
		nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_PROCESS)
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)
		}
	} else if issueItem.State == int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_PROCESS) {
		//只有管理员和执行者可以指定执行者和检查者
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || issueItem.ExecUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			canAssignExecUser = true
			canAssignCheckUser = true
		}
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || issueItem.ExecUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			if unDone == 0 {
				nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_CHECK)
			}
		}
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			if unDone == 0 {
				nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)
			}
		}
	} else if issueItem.State == int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CHECK) {
		//只有管理员或执行者或检查者可以变更检查者
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || issueItem.ExecUserId == sessionItem.UserId || issueItem.CheckUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			canAssignCheckUser = true
		}
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || issueItem.CheckUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_PROCESS)
			if unDone == 0 {
				nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)
			}
		}
	} else if issueItem.State == int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE) {
		nextStateList = append(nextStateList, project_issue_api.ISSUE_STATE_ISSUE_STATE_PROCESS)
	}
	if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
		canRemove = true
	}

	canOptDependence := false
	canOptSubIssue := false

	if sessionItem.UserId == projectItem.OwnerUserId || roleItem.BasicInfo.Admin || sessionItem.UserId == issueItem.CreateUserId ||
		sessionItem.UserId == issueItem.ExecUserId || sessionItem.UserId == issueItem.CheckUserId {
		if issueItem.State != int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE) {
			canOptSubIssue = true
			canOptDependence = true
		}
	}

	if issueItem.State != int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE) {
		if projectItem.OwnerUserId == sessionItem.UserId || issueItem.CreateUserId == sessionItem.UserId || roleItem.BasicInfo.Admin {
			canUpdate = true
		}
	}

	return &project_issue_api.UserIssuePerm{
		CanAssignExecUser:  canAssignExecUser,
		CanAssignCheckUser: canAssignCheckUser,
		NextStateList:      nextStateList,
		CanRemove:          canRemove,
		CanOptDependence:   canOptDependence,
		CanOptSubIssue:     canOptSubIssue,
		CanUpdate:          canUpdate,
	}
}

func (apiImpl *ProjectIssueApiImpl) GetMemberState(ctx context.Context, req *project_issue_api.GetMemberStateRequest) (*project_issue_api.GetMemberStateResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_issue_api.GetMemberStateResponse{
			Code:   project_issue_api.GetMemberStateResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_issue_api.GetMemberStateResponse{
			Code:   project_issue_api.GetMemberStateResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_issue_api.GetMemberStateResponse{
			Code:   project_issue_api.GetMemberStateResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	stateItem, err := project_dao.MemberStateDao.Get(ctx, req.ProjectId, sessionItem.UserId)
	if err != nil {
		return nil, err
	}
	if stateItem.TaskIssueState.Dirty || stateItem.BugIssueState.Dirty {
		stateItem, err = apiImpl.fixUserState(ctx, stateItem)
		if err != nil {
			return nil, err
		}
	}
	return &project_issue_api.GetMemberStateResponse{
		Code:        project_issue_api.GetMemberStateResponse_CODE_OK,
		MemberState: stateItem.ToMemberState(),
	}, nil
}

func (apiImpl *ProjectIssueApiImpl) ListMemberState(ctx context.Context, req *project_issue_api.ListMemberStateRequest) (*project_issue_api.ListMemberStateResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_issue_api.ListMemberStateResponse{
			Code:   project_issue_api.ListMemberStateResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_issue_api.ListMemberStateResponse{
			Code:   project_issue_api.ListMemberStateResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_issue_api.ListMemberStateResponse{
			Code:   project_issue_api.ListMemberStateResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	var memberItemLit []*project_dao.MemberInfoDbItem
	if req.FilterByMemberUserId {
		memberItemLit, err = project_dao.MemberInfoDao.ListMemberById(ctx, req.ProjectId, req.MemberUserIdList)
		if err != nil {
			return nil, err
		}
	} else {
		memberItemLit, err = project_dao.MemberInfoDao.ListMember(ctx, req.ProjectId)
		if err != nil {
			return nil, err
		}
	}
	memberUserIdList := []string{}
	for _, memberItem := range memberItemLit {
		memberUserIdList = append(memberUserIdList, memberItem.MemberUserId)
	}
	stateItemList, err := project_dao.MemberStateDao.ListById(ctx, req.ProjectId, memberUserIdList)
	if err != nil {
		return nil, err
	}
	stateList := []*project_issue_api.MemberState{}
	for _, stateItem := range stateItemList {
		if stateItem.TaskIssueState.Dirty || stateItem.BugIssueState.Dirty {
			stateItem, err = apiImpl.fixUserState(ctx, stateItem)
			if err != nil {
				return nil, err
			}
		}
		stateList = append(stateList, stateItem.ToMemberState())
	}
	return &project_issue_api.ListMemberStateResponse{
		Code:            project_issue_api.ListMemberStateResponse_CODE_OK,
		MemberStateList: stateList,
	}, nil
}

func (apiImpl *ProjectIssueApiImpl) fixUserState(ctx context.Context, stateItem *project_dao.MemberStateDbItem) (*project_dao.MemberStateDbItem, error) {
	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)
	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		if stateItem.TaskIssueState.Dirty {
			createCount, err := issue_dao.IssueDao.CalcCreateCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK)
			if err != nil {
				return nil, err
			}
			unExecCount, err := issue_dao.IssueDao.CalcUnExecCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK)
			if err != nil {
				return nil, err
			}
			unCheckCount, err := issue_dao.IssueDao.CalcUnCheckCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK)
			if err != nil {
				return nil, err
			}
			execDoneCount, err := issue_dao.IssueDao.CalcExecDoneCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK)
			if err != nil {
				return nil, err
			}
			checkDoneCount, err := issue_dao.IssueDao.CalcCheckDoneCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK)
			if err != nil {
				return nil, err
			}
			err = project_dao.MemberStateDao.UpdateTaskIssueState(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, &project_dao.IssueStateDbItem{
				Dirty:          false,
				CreateCount:    createCount,
				UnExecCount:    unExecCount,
				UnCheckCount:   unCheckCount,
				ExecDoneCount:  execDoneCount,
				CheckDoneCount: checkDoneCount,
			})
			if err != nil {
				return nil, err
			}
		}
		if stateItem.BugIssueState.Dirty {
			createCount, err := issue_dao.IssueDao.CalcCreateCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG)
			if err != nil {
				return nil, err
			}
			unExecCount, err := issue_dao.IssueDao.CalcUnExecCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG)
			if err != nil {
				return nil, err
			}
			unCheckCount, err := issue_dao.IssueDao.CalcUnCheckCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG)
			if err != nil {
				return nil, err
			}
			execDoneCount, err := issue_dao.IssueDao.CalcExecDoneCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG)
			if err != nil {
				return nil, err
			}
			checkDoneCount, err := issue_dao.IssueDao.CalcCheckDoneCount(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG)
			if err != nil {
				return nil, err
			}
			err = project_dao.MemberStateDao.UpdateBugIssueState(sessCtx, stateItem.ProjectId, stateItem.MemberUserId, &project_dao.IssueStateDbItem{
				Dirty:          false,
				CreateCount:    createCount,
				UnExecCount:    unExecCount,
				UnCheckCount:   unCheckCount,
				ExecDoneCount:  execDoneCount,
				CheckDoneCount: checkDoneCount,
			})
			if err != nil {
				return nil, err
			}
		}

		stateItem, err := project_dao.MemberStateDao.Get(sessCtx, stateItem.ProjectId, stateItem.MemberUserId)
		if err != nil {
			return nil, err
		}
		return stateItem, nil
	})
	if err != nil {
		return nil, err
	}
	return ret.(*project_dao.MemberStateDbItem), nil
}
